const md5 = require('md5');
const rp = require('request-promise');
const crypto = require('crypto');

module.exports = class extends think.Service {
  async login(code) {
    try {
      // 获取 session
      const options = {
        method: 'GET',
        url: 'https://api.weixin.qq.com/sns/jscode2session',
        qs: {
          grant_type: 'authorization_code',
          js_code: code,
          secret: think.config('wechat.AppSecret'),
          appid: think.config('wechat.AppID')
        }
      };

      let sessionData = await rp(options);

      sessionData = JSON.parse(sessionData);
      if (!sessionData.openid) {
        return null;
      }
      return sessionData;
    } catch (e) {
      return null;
    }
  }
  async getUserPhoneData(encrypted, ivData, code) {
    const sessionData = await this.login(code);
    if (!sessionData) {
      return null;
    }
    const sessionKey = Buffer.from(sessionData.session_key, 'base64');
    const encryptedData = Buffer.from(encrypted, 'base64');
    const iv = Buffer.from(ivData, 'base64');
    let decoded;
    try {
      // 解密
      const decipher = crypto.createDecipheriv('aes-128-cbc', sessionKey, iv);
      // 设置自动 padding 为 true，删除填充补位
      decipher.setAutoPadding(true);
      decoded = decipher.update(encryptedData, 'binary', 'utf8');
      decoded += decipher.final('utf8');

      decoded = JSON.parse(decoded);
    } catch (err) {
      console.log(err);
      throw new Error('Illegal Buffer1');
    }

    if (decoded.watermark.appid !== think.config('wechat.AppID')) {
      throw new Error('Illegal Buffer2');
    }

    return decoded;
  }

  async getWxcode(id) {
    const config = think.config('wechat');
    this.AppID = config.AppID;
    this.AppSecret = config.AppSecret;
    this.host = config.host;
    this.baseURL = config.baseURL;
    this.apiURL = `${this.host}${this.baseURL}`;

    const url = `${this.apiURL}/token?grant_type=client_credential&appid=${this.AppID}&secret=${this.AppSecret}`;
    const result = await this.fetch(url).then(res => res.json());
    try {
      // 获取 session
    // https://api.weixin.qq.com/wxa/getwxacodeunlimit
      const options = {
        method: 'POST',
        uri: `https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=${result.access_token}`,
        body: {
          'page': `pages/sale/sale`,
          'scene': `productId,${id}`,
          // 'env_version': 'trial',
          'env_version': 'release',
          'check_path': false
        },
        encoding: 'base64',
        json: true
      };
      const data = await rp(options);
      return data;
    } catch (e) {
      return null;
    }
  }

  async getWxToken() {
    const config = think.config('wechat');
    this.AppID = config.AppID;
    this.AppSecret = config.AppSecret;
    this.host = config.host;
    this.baseURL = config.baseURL;
    this.apiURL = `${this.host}${this.baseURL}`;

    const url = `${this.apiURL}/token?grant_type=client_credential&appid=${this.AppID}&secret=${this.AppSecret}`;
    const result = await this.fetch(url).then(res => res.json());
    return result.access_token;
  }

  /**
   * 解析微信登录用户数据
   * @param access_token
   * @param openid
   * @returns {Promise.<string>}
   */
  async getUserInfoData(accessToken, openid) {
    try {
      // 获取 session
      const options = {
        method: 'GET',
        url: 'https://api.weixin.qq.com/sns/userinfo',
        qs: {
          access_token: accessToken,
          openid: openid,
          lang: 'zh_CN'
        }
      };

      let userInfoData = await rp(options);
      userInfoData = JSON.parse(userInfoData);
      if (think.isEmpty(userInfoData)) {
        return null;
      }
      return userInfoData;
    } catch (e) {
      return null;
    }
  }

  /**
   * 统一下单
   * @param payInfo
   * @returns {Promise}
   */
  createUnifiedOrder(payInfo) {
    const WeiXinPay = require('weixinpay');
    const weixinpay = new WeiXinPay({
      appid: think.config('wechat.AppID'), // 微信小程序appid
      openid: payInfo.openid, // 用户openid
      mch_id: think.config('wechat.mch_id'), // 商户帐号ID
      partner_key: think.config('wechat.partner_key') // 秘钥
    });
    return new Promise((resolve, reject) => {
      weixinpay.createUnifiedOrder({
        body: payInfo.body,
        out_trade_no: payInfo.out_trade_no,
        total_fee: payInfo.total_fee,
        spbill_create_ip: payInfo.spbill_create_ip,
        notify_url: think.config('wechat.notify_url'),
        trade_type: 'JSAPI'
      }, (res) => {
        if (res.return_code === 'SUCCESS' && res.result_code === 'SUCCESS') {
          const returnParams = {
            'appid': res.appid,
            'timeStamp': parseInt(Date.now() / 1000) + '',
            'nonceStr': res.nonce_str,
            'package': 'prepay_id=' + res.prepay_id,
            'signType': 'MD5',
            'attach': payInfo.attach
          };
          console.log(returnParams, 123);
          const paramStr = `appId=${returnParams.appid}&nonceStr=${returnParams.nonceStr}&package=${returnParams.package}&signType=${returnParams.signType}&timeStamp=${returnParams.timeStamp}&key=` + think.config('wechat.partner_key');
          returnParams.paySign = md5(paramStr).toUpperCase();
          resolve(returnParams);
        } else {
          reject(res);
        }
      });
    });
  }

  /**
   * 生成排序后的支付参数 query
   * @param queryObj
   * @returns {Promise.<string>}
   */
  buildQuery(queryObj) {
    const sortPayOptions = {};
    for (const key of Object.keys(queryObj).sort()) {
      sortPayOptions[key] = queryObj[key];
    }
    let payOptionQuery = '';
    for (const key of Object.keys(sortPayOptions).sort()) {
      payOptionQuery += key + '=' + sortPayOptions[key] + '&';
    }
    payOptionQuery = payOptionQuery.substring(0, payOptionQuery.length - 1);
    return payOptionQuery;
  }

  /**
   * 对 query 进行签名
   * @param queryStr
   * @returns {Promise.<string>}
   */
  signQuery(queryStr) {
    queryStr = queryStr + '&key=' + think.config('wechat.partner_key');
    const md5 = require('md5');
    const md5Sign = md5(queryStr);
    return md5Sign.toUpperCase();
  }

  /**
   * 处理微信支付回调
   * @param notifyData
   * @returns {{}}
   */
  payNotify(notifyData) {
    if (think.isEmpty(notifyData)) {
      return false;
    }

    const notifyObj = {};
    let sign = '';
    for (const key of Object.keys(notifyData)) {
      if (key !== 'sign') {
        notifyObj[key] = notifyData[key][0];
      } else {
        sign = notifyData[key][0];
      }
    }
    if (notifyObj.return_code !== 'SUCCESS' || notifyObj.result_code !== 'SUCCESS') {
      return false;
    }
    const signString = this.signQuery(this.buildQuery(notifyObj));
    if (think.isEmpty(sign) || signString !== sign) {
      return false;
    }
    return notifyObj;
  }
};
